/**
 *******************************************************************************
 * \brief   CAS EBX Project Work "Kaffeemaschine"
 *
 * \file    pool.c
 * \version 1.0
 * \date    30.06.2010
 * \author  Baehler Thomas, Liechti Beat, Sauter Marc
 * \author  Vuksic Valentina, Zbinden Rene
 *
 * \remark  Last Modifications:
 * \remark  V1.0, 30.06.2010   Initial release
 * \remark  V1.1, 04.07.2010   Valentina's implementation of the pool
 * \remark  V1.2  07.07.2010   Changed interface to use create/delete functions
 * \remark  V1.3  12.07.2010   Updating documentation for doxygen
 *******************************************************************************
 **/

/*
 *******************************************************************************
 * Imports
 *******************************************************************************
 */

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>

#include <semaphore.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/mman.h>

#include "pool.h"

/*
 *******************************************************************************
 * Definitions
 *******************************************************************************
 */

#define ROOT_PATH	"/pool_"

/*
 *******************************************************************************
 * Local Variables
 *******************************************************************************
 */

/* -- nothing -- */

/*
 *******************************************************************************
 * Local Function Declaration
 *******************************************************************************
 */

/* -- nothing -- */

/*
 *******************************************************************************
 * Global Function Definition
 *******************************************************************************
 */

/**
 *******************************************************************************
 * createPool
 *
 * \brief creates a shared memory. maps data into the shared memory and
 *        creates a semaphore to synchronize writing and reading
 *        the shared memory.
 *
 * \param name The name of the pool
 * \param data Pointer to the initial data
 * \param size The size of the data
 *
 * \return int Zero if pool has been created successfully, -1 otherwise
 *
 *******************************************************************************
 **/

int createPool(char *name, void *data, int size)
{
	int ret = -1;
	/* create shared memory */
	int pathlen = strlen(ROOT_PATH) + strlen(name) + 1;
	char *path = alloca(pathlen);
	sprintf(path, "%s%s", ROOT_PATH, name);
	shm_unlink(path);
	int shm = shm_open(path, O_RDWR | O_CREAT, S_IRWXU | S_IRWXG);
	if (shm < 0)
	{
		perror("Error opening shared memory");
	}
	else
	{
		/* define size of shared memory */
		if (ftruncate(shm, size) == -1)
		{
			perror("Error setting size of shared memory");
		}
		else
		{
			/* map data into shared memory */
			void *datatmp = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, shm, 0);
			if (datatmp == NULL)
			{
				perror("Error mapping shared memory");
			}
			else
			{
				/* copy initial data */
				memcpy(datatmp, data, size);
				/* create semaphore */
				sem_t * semaphore;
				/* Make sure restarts work */
				sem_unlink(path);
				if ((semaphore = sem_open(path, O_RDWR | O_CREAT, S_IRWXU | S_IRWXG, 1)) < 0)
				{
					perror("Error creating semaphore!");
				}
				else
				{
					printf("created shared memory and semaphore %s\n", path);
					sem_close(semaphore);
					ret = 0;
				}
			}
		}
		close(shm);
	}
	return ret;
}

/**
 *******************************************************************************
 * deletePool
 *
 * \brief unlink shared memory and the semaphore
 *
 * \param name The name of the pool
 *
 *******************************************************************************
 **/

void deletePool(char *name)
{
	int pathlen = strlen(ROOT_PATH) + strlen(name) + 1;
	char *path = alloca(pathlen);
	sprintf(path, "%s%s", ROOT_PATH, name);
	/* delete semaphore */
	sem_unlink(path);
	/* delete shared memory */
	shm_unlink(path);
}

/**
 *******************************************************************************
 * openPool
 *
 * \brief open shared memory, map the shared memory into the address space
 *        and open a semaphore to synchronize writing and reading the
 *        shared memory.
 *
 * \param name The name of the channel
 * \param size The size of the data
 *
 * \return tPool* Pointer to pool ADT structure, NULL if error
 *
 *******************************************************************************
 **/

tPool *openPool(char *name, int size)
{
	// create pool 
	tPool *pool = malloc(sizeof(tPool));
	if (pool != NULL)
	{		
		memset(pool, 0, sizeof(tPool));
		pool->size = size;
		pool->name = strdup(name);
		if (pool->name != NULL)
		{
			int pathlen = strlen(ROOT_PATH) + strlen(name) + 1;
			pool->path = malloc(pathlen);
			if (pool->path != NULL)
			{
				sprintf(pool->path, "%s%s", ROOT_PATH, name);
				// open shared memory
				pool->shm = shm_open(pool->path, O_RDWR, 0);
				if (pool->shm < 0)
				{
					perror("Error opening shared memory");
					free(pool->path);
					free(pool->name);
					free(pool);
					pool = NULL;
				}
				else
				{
					pool->data = mmap(NULL, pool->size, PROT_READ | PROT_WRITE, MAP_SHARED, pool->shm, 0);
					if (pool->data == NULL)
					{
						perror("Error mapping shared memory");
						close(pool->shm);
						free(pool->path);
						free(pool->name);
						free(pool);
						pool = NULL;
					}
					else
					{
						/* open semaphore */
						if ((pool->sem = sem_open(pool->path, O_RDWR)) == SEM_FAILED)
						{
							perror("Error opening semaphore!");
							close(pool->shm);
							free(pool->path);
							free(pool->name);
							free(pool);
							pool = NULL;
						}
					}
				}
			}
			else
			{
				free(pool->name);
				free(pool);
				pool = NULL;
			}
		}
		else
		{
			free(pool);
			pool = NULL;
		}
	}
	return pool;
}

/**
 *******************************************************************************
 * writeToPool
 *
 * \brief writes a data structure to a shared memory. The thread-safe
 *        write process is using a semaphore.
 *
 * \param pool Pointer to pool ADT structure
 * \param data Pointer to the data to write
 *
 * \return int Bytes written, error if < 0
 *
 *******************************************************************************
 **/

int writeToPool(tPool *pool, void *data)
{	
	int ret = 0;
	
	// lock semaphore
	if (sem_wait(pool->sem) < 0)
	{
		perror("Error getting hold on the semaphore");
		ret = -1;
	}
	else
	{
		// write data to shared memory
		if (data != NULL)
		{
			memcpy(pool->data, data, pool->size);
			ret = pool->size;
		}
		
		// release semaphore
		if (sem_post(pool->sem) < 0)
		{
			perror("Error releasing semaphore");
			ret = -1;
		}
	}
		
	return ret;
}

/**
 *******************************************************************************
 * readFromPool
 *
 * \brief reads out a data structure from a shared memory. The thread-safe
 *        read process is using a semaphore.
 *
 * \param pool Pointer to pool ADT structure
 * \param data Pointer to destination for data
 *
 * \return bytes read, error if < 0
 *
 *******************************************************************************
 **/

int readFromPool(tPool *pool, void *data)
{
	int ret = 0;
	// lock semaphore
	if (sem_wait(pool->sem) < 0)
	{
		perror("Error getting hold on the semaphore");
	}
	else
	{
		// read data from shared memory
		if (data != NULL)
		{
			memcpy(data, pool->data, pool->size);
			ret = pool->size;
		}
		
		// release semaphore
		if (sem_post(pool->sem) < 0)
		{
			perror("Error releasing semaphore");
		}
	}
	return ret;
}

/**
 *******************************************************************************
 * closePool
 *
 * \brief closes the shared memory, as well as the semaphore.
 *        Resources are deallocated.
 *
 * \param pool Pointer to pool ADT structure
 *
 * \return int Error code of -1 for error and 0 for success
 *
 *******************************************************************************
 **/

int closePool(tPool *pool)
{
	int ret = 0;
	
	// close shared memory
	close(pool->shm);
	// close semaphore
	sem_close(pool->sem);
	
	// cleanup pool structure
	free(pool->path);
	free(pool->name);
	free(pool);
	pool = NULL;
	
	return ret;
}
